package org.easyb.junit;

import static org.easyb.BehaviorRunner.getBehaviors;
import static org.easyb.junit.RunProperties.isEclipse;
import static org.junit.runner.Description.createSuiteDescription;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.easyb.domain.Behavior;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;

public class EasybJUnitRunner extends Runner {
   private final RunNotifierReplay runNotifierReplay = new RunNotifierReplay();
   private final DescriptionCreator descriptionCreator;
   private Description description;
   private List<Behavior> behaviors;
   private final EasybSuite suite;

   public EasybJUnitRunner(Class<? extends EasybSuite> testClass) throws Exception {
      suite = testClass.newInstance();
      descriptionCreator = new DescriptionCreator(suite.baseDir());
   }

   public Description getDescription() {
      if (description == null) {
         description = createSuiteDescription(suite.description());
         if(isEclipse())
            executeBehaviors(runNotifierReplay);
      }
      return description;
   }

   private void executeBehaviors(RunNotifier notifier) {
      for (Behavior behavior : behaviors()) {
         Description behaviorDescription = descriptionCreator.create(behavior);
         description.addChild(behaviorDescription);
         executeBehavior(behavior, behaviorDescription, notifier);
      }
   }

   private void executeBehavior(Behavior behavior, Description behaviorDescription,RunNotifier notifier) {
      try {
         behavior.execute(new JUnitExecutionListener(behaviorDescription, notifier));
      } catch (IOException e) {
         throw new RuntimeException(e);
      }
   }

   public void run(RunNotifier notifier) {
      if(isEclipse())
         runNotifierReplay.replay(notifier, suite.trackTime());
      else
         executeBehaviors(notifier);
   }

   private List<Behavior> behaviors() {
      return behaviors != null ? behaviors : (behaviors = getBehaviors(getFilePaths()));
   }

   private String[] getFilePaths() {
      List<String> filePaths = new ArrayList<String>();
      listFiles(suite.searchDir(), filePaths);
      return filePaths.toArray(new String[filePaths.size()]);
   }

   private void listFiles(File dir, List<String> files) {
      for (File file : dir.listFiles()) {
         if (file.isDirectory()) {
            listFiles(file, files);
         } else if (isBehavior(file)) {
            files.add(file.getAbsolutePath());
         }
      }
   }

   private boolean isBehavior(File file) {
      return file.getName().endsWith(".story") || file.getName().endsWith(".specification");
   }
}
